package edu.hawaii.client;

import java.util.List;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.HistoryListener;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.TabPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * Defines widgets for the Class Availability application.  It defines drop-down
 * lists, interaction between them, buttons, tabbed panels etc basically the user
 * interface.
 * 
 * @author Kiet Huynh
 * @author Brandon Lee
 */
public class ClsClassAvailability extends Composite implements ChangeListener, 
                                    ClickListener, HistoryListener {
  
  private static final String column_label_style = "courseColumn";
  private static final String campus_name_style = "campusName";
  
  private VerticalPanel vert = new VerticalPanel(); // main panel
  private VerticalPanel dropDownListsPanel = new VerticalPanel();
  private VerticalPanel coursePanel = new VerticalPanel();
  //private VerticalPanel verpanelFocusReqs = new VerticalPanel();
  
  private HorizontalPanel campusNamePanel = new HorizontalPanel();
  private HorizontalPanel subjectNamePanel = new HorizontalPanel();
  private HorizontalPanel campusSemesterPanel = new HorizontalPanel();
  private HorizontalPanel requirementsPanel = new HorizontalPanel();
  private HorizontalPanel courseTablePanel = new HorizontalPanel();
  private HorizontalPanel buttonHorzPannel = new HorizontalPanel();
  
  private Button searchButton = new Button("Search");
  private HTML horizontalLine = new HTML("<hr>");
  private final HTML status = new HTML();

  //This table will contains the courses found.
  private final FlexTable table = new FlexTable();
 
  //the containers components, the drop-downs in html.
  private ListBox cbCampuses = new ListBox();
  private ListBox cbSemesters = new ListBox();
  private ListBox cbSubjects = new ListBox();
  private ListBox cbFoundationReqs = new ListBox();
  private ListBox cbDiversificationReqs = new ListBox();

  /*
  //this is commented out cause in procedure call can only search one attribute at a time.
  private CheckBox checkBoxETH = new CheckBox("ETH");
  private CheckBox checkBoxHAP = new CheckBox("HAP");
  private CheckBox checkBoxOC = new CheckBox("OC");
  private CheckBox checkBoxWI = new CheckBox("WI");*/
  
  //testing as a ddlist than check boxes
  private ListBox focusDD = new ListBox();
  
  
  /**
   * Initializes widgets and lay them out on the screen.
   *
   */
  public ClsClassAvailability() {
    
    History.addHistoryListener(this);
    
    //setup
    Widget institutionPanel = new Widget(); // need a new object???
    institutionPanel = createVerticalPanel("Institution", cbCampuses);
    
    Widget semesterPanel = createVerticalPanel("Semester", cbSemesters);
    Widget subjectPanel = createVerticalPanel("Subject", cbSubjects);
    
    vert.setSpacing(1);
    this.dropDownListsPanel.setSpacing(4);
    this.dropDownListsPanel.setStyleName("dropDownPanel");
    this.buttonHorzPannel.setSpacing(20);

    //sets components visible and asigns an action listener to it.
    cbCampuses.setVisibleItemCount(1); //how many in dropdown can see.
    cbCampuses.addChangeListener(this);
    
    cbSemesters.setVisibleItemCount(1);
    cbSemesters.setSize("170", "1.5em");
    cbSemesters.addChangeListener(this);
    
    cbSubjects.setVisibleItemCount(1);
    cbSubjects.setSize("170", "1.5em");
    cbSubjects.addChangeListener(this);
    
    this.cbFoundationReqs.setVisibleItemCount(1);
    this.cbDiversificationReqs.setVisibleItemCount(1);
    
    //adding campus & semester drop down lists.
    this.campusSemesterPanel.add(institutionPanel);
    this.campusSemesterPanel.add(semesterPanel);
    this.dropDownListsPanel.add(makeLabel("1. Select a campus, then select a semester.", 
        "numberingSystem"));
    this.dropDownListsPanel.add(this.campusSemesterPanel);
    
    //setup and adding requirement tabbed pannel.
    TabPanel tp = new TabPanel(); //tabbed panel for the focus/attributes
    
    Widget foundationsReqPanel = createVerticalPanel("Foundations Requirements", 
        this.cbFoundationReqs);
    Widget diversificationReqPanel = createVerticalPanel("Diversification Requirements", 
        this.cbDiversificationReqs);
    Widget focusReqPanel = createVerticalPanel("Focus Requirements", 
        this.focusDD);
    
    //add into tabbed pannel
    tp.add(foundationsReqPanel, " Foundation ");
    tp.add(diversificationReqPanel, " Diversification ");
    tp.add(focusReqPanel, " Focus ");
    tp.selectTab(0); //sets the initial focus on the that particular tab
    
    this.requirementsPanel.add(tp);
    this.requirementsPanel.setSpacing(10); //some styles stuff
    this.requirementsPanel.setWidth("100%"); //some styles stuff

    this.dropDownListsPanel.add(makeLabel(
        "2. Select the requirements, choose \"Not Applicable\" if they do not matter to you", 
        "numberingSystem"));
    this.dropDownListsPanel.add(requirementsPanel);
    
    //adding subjects drop down list
    this.dropDownListsPanel.add(makeLabel("3. Select a subject.", "numberingSystem"));
    this.dropDownListsPanel.add(subjectPanel);
   
    // The Button widget
    searchButton.addClickListener(this);
 
    buttonHorzPannel.add(searchButton);
    //setStatusText("Init");
    buttonHorzPannel.add(this.status);
    
    courseTablePanel.add(table);
    table.addStyleName("results");
    coursePanel.add(campusNamePanel);
    coursePanel.add(subjectNamePanel);
    coursePanel.add(courseTablePanel);
    
    vert.add(dropDownListsPanel);
    vert.add(buttonHorzPannel);
    vert.add(this.coursePanel);

    
    String initToken = History.getToken();
    if (initToken.length() > 0) {
      onHistoryChanged(initToken);
    }
    
    initWidget(vert);
    
    //add a list of campuses to the campus drop-down list.
    cbCampuses.addItem("Select a campus", "invalid");
    cbCampuses.addItem("Hawaii Community College", "HAW");
    cbCampuses.addItem("Honolulu Community College", "HON");
    cbCampuses.addItem("Kapi'olani Community College", "KAP");
    cbCampuses.addItem("Kauai Community College", "KAU");
    cbCampuses.addItem("Leeward Community College", "LEE");
    cbCampuses.addItem("Maui Community College", "MAU");
    cbCampuses.addItem("University of Hawaii West Oahu", "WOA");
    cbCampuses.addItem("University of Hawaii at Hilo", "HIL");
    cbCampuses.addItem("University of Hawaii at Manoa", "MAN");
    cbCampuses.addItem("Windward Community College", "WIN");
    
    //add a list of General Education Foundataions Requirements to 
    //the cbFoundationReqs drop-down list.
    this.cbFoundationReqs.addItem("Not Applicable", "Invalid");
    this.cbFoundationReqs.addItem("FGA - Foundation GMP-A", "FGA");
    this.cbFoundationReqs.addItem("FGB - Foundation GMP-B", "FGB");
    this.cbFoundationReqs.addItem("FGC - Foundation GMP-C", "FGC");
    this.cbFoundationReqs.addItem("FS - Foundation Symbolic Reasoning", "FS");
    this.cbFoundationReqs.addItem("FW - Foundation Written Communication", "FW");
    
    //add a list of General Education Diversification Requirements to the 
    //cbDiversificationReqs drop-down list.
    this.cbDiversificationReqs.addItem("Not Applicable", "Invalid");
    this.cbDiversificationReqs.addItem("DA - Diversification-Arts", "DA");
    this.cbDiversificationReqs.addItem("DB - Diversification-Biological Science", "DB");
    this.cbDiversificationReqs.addItem("DH - Diversification-Humanities", "DH");
    this.cbDiversificationReqs.addItem("DL - Diversification-Literatures", "DL");
    this.cbDiversificationReqs.addItem("DP - Diversification-Physical Science", "DP");
    this.cbDiversificationReqs.addItem("DS - Diversification-Social Science", "DS");
    this.cbDiversificationReqs.addItem("DY - Diversification-Laboratory", "DY");
    
    //populating list
    this.focusDD.addItem("Not Applicable", "Invalid");
    this.focusDD.addItem("ETH - Contemporary Ethical Issues", "ETH");
    this.focusDD.addItem("HAP - Hawaiian, Asian, and Pacific Issues", "HAP");
    this.focusDD.addItem("OC  - Oral Communication", "OC");
    this.focusDD.addItem("WI  - Writing Intensive", "WI");
  }
  
  /** 
   * Creates a horizontal panel that consists of a label on the left and
   * a drop-down list on the right.
   * @param label Text that will be displayed on the left of the panel.
   * @param comboBox A drop-down list that will be placed on the right
   * of the panel.
   * @return A horizontal panel.
   */
  public Widget createHorizontalPanel(String label, ListBox comboBox) {
    HorizontalPanel panel = new HorizontalPanel();
    panel.setSpacing(8);
    panel.add(new HTML(label));
    panel.add(comboBox);
    return panel;
  }
 
  /** 
   * Creates a horizontal panel that consists of a label on the left and
   * a drop-down list on the right.
   * @param label Text that will be displayed on the left of the panel.
   * @param comboBox A drop-down list that will be placed on the right
   * of the panel.
   * @return A horizontal panel.
   */
  public Widget createVerticalPanel(String label, ListBox comboBox) {
    VerticalPanel panel = new VerticalPanel();
    panel.setSpacing(8);
    panel.add(new HTML(label));
    panel.add(comboBox);
    return panel;
  }
  
  /**
   * Defines the dependency between drop-down lists.
   * @param sender The drop-down list that causes the change.
   */
  public void onChange(Widget sender) {
    if (sender.equals(cbCampuses)) {
      clearStatusText();
      cbSemesters.clear();
      this.cbSubjects.clear();
      GetDataServiceAsync respService = (GetDataServiceAsync) 
                                                GWT.create(edu.hawaii.client.GetDataService.class);
      ServiceDefTarget endpoint = (ServiceDefTarget) respService;
      String moduleRelativeURL = GWT.getModuleBaseURL() + "getDataService";
      endpoint.setServiceEntryPoint(moduleRelativeURL);
      
      //preventing user input durring query
      this.cbSemesters.setStyleName("loadingStatus");
      this.cbSemesters.addItem("Loading...");
      int selectedCampus = this.cbCampuses.getSelectedIndex();
      cbCampuses.setEnabled(false); //disable dd-list till proceedure call done
      respService.getSemester(this.cbCampuses.getValue(selectedCampus), new AsyncCallback() {
        public void onFailure(Throwable caught) {
          setStatusText("Server request raised an error"); // doesn't work now
        }  
       
        public void onSuccess(Object result) {
          List semesters = (List) result;
          
          // semesters.size() == 0 when there is a problems with the database
          if (semesters.size() == 0) {
            setStatusText("An error has encountered.");
            cbCampuses.setEnabled(true);
            cbSemesters.removeStyleName("loadingStatus");
            cbSemesters.clear();
            cbCampuses.setSelectedIndex(0);
          }
          else {
            cbCampuses.setEnabled(true); // call done re-abling ddlist
            cbSemesters.removeStyleName("loadingStatus");
            cbSemesters.removeItem(0);
            cbSemesters.addItem("Select a semester", "invalid");
            for (int i = (semesters.size() - 1); i >= 0; i--) {
              Semester record = (Semester) semesters.get(i);
              cbSemesters.addItem(record.getSemesterText(), new Integer(record.getSemesterKey())
                  .toString());
            }
          }
        }
      }); 
    }
    
    if (sender.equals(cbSemesters)) {
      clearStatusText();
      cbSubjects.clear();
      GetDataServiceAsync respService = (GetDataServiceAsync) 
      GWT.create(edu.hawaii.client.GetDataService.class);
      ServiceDefTarget endpoint = (ServiceDefTarget) respService;
      String moduleRelativeURL = GWT.getModuleBaseURL() + "getDataService";
      endpoint.setServiceEntryPoint(moduleRelativeURL);

      // preventing user input durring query
      this.cbSubjects.setStyleName("loadingStatus");
      this.cbSubjects.addItem("Loading...");
      int campusSelectedIndex = this.cbCampuses.getSelectedIndex();
      int semesterSelectedIndex = this.cbSemesters.getSelectedIndex();
      
      // disable dd-list till proceedure call done
      cbSemesters.setEnabled(false); 
      cbCampuses.setEnabled(false);
      respService.getCourseNameByCampus(this.cbCampuses.getValue(campusSelectedIndex), 
          this.cbSemesters.getValue(semesterSelectedIndex), 
          new AsyncCallback() {
        
        public void onFailure(Throwable caught) {

          displayFormattedMessage(
              "Server request raised an error; Java exception : " + caught == null ? 
              "An unknown exception"
                  : caught.getMessage(), "red");
        }  
       
        public void onSuccess(Object result) {
          List courseName = (List) result;
          if (courseName.size() == 0) {
            setStatusText("An error has encountered.");
            cbSemesters.setEnabled(true); //done enable dd-list
            cbCampuses.setEnabled(true);
            cbSubjects.removeStyleName("loadingStatus");
            cbSubjects.clear();
            cbSemesters.setSelectedIndex(0);
          }
          else {
            cbSemesters.setEnabled(true); // proceedure call done enable dd-list
            cbCampuses.setEnabled(true);
            cbSubjects.removeStyleName("loadingStatus");
            cbSubjects.removeItem(0);
            for (int i = 0; i <= courseName.size() - 1; i++) {
              CourseNameByCampus record = (CourseNameByCampus) courseName.get(i);
              cbSubjects.addItem(record.getCourseName());
            }
          }
        }
      }); 
    } 
  }
  
  /**
   * Handles the click event of the "Search" Button.
   * @param sender The button that is clicked.
   */
  public void onClick(Widget sender) {
    int selectedCampus = this.cbCampuses.getSelectedIndex();
    int selectedSubject = this.cbSubjects.getSelectedIndex();
    int selectedSemester = this.cbSemesters.getSelectedIndex();

    if ((selectedCampus == -1) || (selectedSemester == -1) || (selectedSubject < 1)) {
      String popUpMessage = "You must select a campus, a subject, and a semester";
      MyPopup p = new MyPopup(popUpMessage);
      int left = sender.getAbsoluteLeft() + 10;
      int top = sender.getAbsoluteTop() + 10;
      p.setPopupPosition(left, top);
      p.show();
    }

    else {
      String campus = this.cbCampuses.getItemText(selectedCampus);
      String semester = this.cbSemesters.getItemText(selectedSemester);
      String campusValue = this.cbCampuses.getValue(selectedCampus);
      String semesterValue = this.cbSemesters.getValue(selectedSemester);
      String subject = this.cbSubjects.getItemText(selectedSubject);
      showCourses(campus, semester, subject, campusValue, semesterValue);
    }
  } 
  
  /**
   * Displays the courses found in a flex table.
   * 
   * @param campus The full name of the selected campus such as University 
   * of Hawaii at Manoa.
   * @param semester The selected semester text such as Fall 2006 and Spring 2007.
   * @param subject The selected subject such as HIST, ICS.
   * @param campusValue The string that represents the campus.  Usually, it is the 
   * first 3 letters of the campus name such as HON for Honolulu Community Collge.
   * @param semesterValue The value of the selected semester such as 200710 for Fall 2006.
   */
  public void showCourses(String campus, String semester, String subject, 
      String campusValue, String semesterValue) {
    History.newItem(campus + "?" + semester + "?" + subject + "?" + campusValue + "?" +
        semesterValue);
    this.campusNamePanel.clear();
    this.subjectNamePanel.clear();
    if (vert.getWidget(2) == horizontalLine) {
      vert.remove(horizontalLine);
    }
    // to remove rows from flex table, start remove from the last row
    for (int i = table.getRowCount() - 1; i > 0; i--) {
      this.table.removeRow(i);
    }
    vert.insert(horizontalLine, 3);

    this.campusNamePanel.add(makeLabel(semester + " -- "  + campus, campus_name_style));
    this.subjectNamePanel.add(makeLabel(subject, campus_name_style));
    
    GetDataServiceAsync respService = (GetDataServiceAsync) GWT
        .create(edu.hawaii.client.GetDataService.class);
    ServiceDefTarget endpoint = (ServiceDefTarget) respService;
    String moduleRelativeURL = GWT.getModuleBaseURL() + "getDataService";
    endpoint.setServiceEntryPoint(moduleRelativeURL);
    
    //  disable dd-list till proceedure call done
    setStatusText("Loading...");
    this.cbCampuses.setEnabled(false);
    this.cbSemesters.setEnabled(false);
    this.cbSubjects.setEnabled(false);
    respService.getAvailableSeatsBySection("500", campusValue, semesterValue, 
        "%", 500, subject, "%", new AsyncCallback() {

          public void onFailure(Throwable caught) {
            displayFormattedMessage(
                "Server request raised an error; Java exception : " 
                + caught == null ? "An unknown exception"
                    : caught.getMessage(), "red");
          }

          public void onSuccess(Object result) {
            clearStatusText();
            cbCampuses.setEnabled(true);
            cbSemesters.setEnabled(true);
            cbSubjects.setEnabled(true);
            List courses = (List) result;
            if (courses.size() == 0) {
              setStatusText("An error has encountered.");
            }
            else {
              // Label Columns of the course table
              table.getRowFormatter().setStyleName(0, column_label_style);
              table.setText(0, 0, "Focus");
              table.setText(0, 1, "CRN");
              table.setText(0, 2, "Course");
              table.setText(0, 3, "Section");
              table.setText(0, 4, "Title");
              table.setText(0, 5, "Credits");
              table.setText(0, 6, "Instructor");
              table.setText(0, 7, "Seats Available");
              table.setText(0, 8, "Days");
              table.setText(0, 9, "Times");
              table.setText(0, 10, "Room");
              table.setText(0, 11, "Dates");

              table.setBorderWidth(0);

              
              for (int i = 1; i <= courses.size(); i++) {
                AvailableSeatsBySection record = (AvailableSeatsBySection) courses.get(--i);
                ++i;
                table.setText(i, 0, record.getATTRIBUTE());
                table.setText(i, 1, record.getCRN());
                table.setText(i, 2, record.getCOURSENAME() + record.getCOURSENUMBER());
                table.setText(i, 3, record.getSECTION());
                table.setText(i, 4, record.getCOURSEDESCRIPTION());
                table.setText(i, 5, record.getCOURSECREDITS());
                table.setText(i, 6, record.getINSTRUCTOR());
                table.setText(i, 7, record.getTOTALSEATAVAILABLE());
                table.setText(i, 8, record.getDAYS());
                table.setText(i, 9, record.getTIME());
                table.setText(i, 10, record.getLOCATION());
                table.setText(i, 11, Integer.toString(i));

                if ((i > 0) && ((i % 2) == 0)) { // even row, reset backgroud color
                  table.getRowFormatter().setStyleName(i, "courseEvenRow");
                }
              }
            }
          }
        });
  }
 
  /**
   * Makes a label.
   * @param caption Text to be display.
   * @param style The caption style configured in ClassAvailability.css.
   * @return The lable.
   */
  private HTML makeLabel(String caption, String style) {
    HTML html = new HTML(caption);
    html.setStyleName(style);
    return html;
  }
  
  /**
   * Formats a message to be displayed.
   * @param message The message to display.
   * @param color The color of the message.
   */
  private void displayFormattedMessage(String message, String color) {
    Element el = DOM.getElementById("status");
    if (el != null) {
      DOM.setStyleAttribute(el, "font-family", "ariel, san-serif");
      DOM.setStyleAttribute(el, "font-size", "1.2em");
      DOM.setStyleAttribute(el, "color", color);
      DOM.setInnerHTML(el, message);
    }
  }
  
  /**
   * A very simple popup that closes automatically when you click off of it.
   */
  private static class MyPopup extends PopupPanel {
    
    /**
     * Default contructor to initialize the popup widget.
     * @param popUpMessage The popupMessage.
     */
    public MyPopup(String popUpMessage) {
      super(true);

      HTML contents = new HTML(popUpMessage);
      contents.setWidth("128px");
      setWidget(contents);

      setStyleName("popUp");
    }
  }
  
  /**
   * Sets the text of the "status" widget.  This method is usually called when 
   * data is loading from the server.
   * @param text The text to be display in the "status" widget.
   */
  public void setStatusText(String text) {
    status.setStyleName("loadingStatus");
    status.setText(text);
  }
  
  /**
   * Clears the text of the "status" widget.  This method is called when data
   * is loaded successfully.
   *
   */
  public void clearStatusText() {
    status.setHTML("");
  }
  
  /**
   * Manages browser Hisory and Bookmark.
   * @param token The history token which contains parameters needed by the database.  
   * For example, token can be "man 200720 ICS".  If the token is "", the application is
   * fresh started.
   */
  public void onHistoryChanged(String token) {
    if (token == "") {
      return;
    }
    else {
      String[] dbParameters = token.split("\\?");
      String campus = dbParameters[0];
      String semester = dbParameters[1];
      String subject = dbParameters[2];
      String campusValue = dbParameters[3];
      String semesterValue = dbParameters[4];
      this.showCourses(campus, semester, subject, campusValue, semesterValue);   
    }
  }
}

